package com.legendshop.openportal.sdk.client;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.legendshop.openportal.sdk.cache.OpenCache;
import com.legendshop.openportal.sdk.client.http.HttpResponseHandler;
import com.legendshop.openportal.sdk.config.OpenConfig;
import com.legendshop.openportal.sdk.model.BaseRequest;
import com.legendshop.openportal.sdk.model.OpenResult;
import com.legendshop.openportal.sdk.model.constants.CommonConstants;
import com.legendshop.openportal.sdk.model.response.AuthResponse;
import com.legendshop.openportal.sdk.registry.OpenCacheRegistry;
import com.legendshop.openportal.sdk.registry.OpenConfigProviderRegistry;
import com.legendshop.openportal.sdk.utils.TokenUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;

/**
 * @author jzh
 * @version 1.0.0
 * @title OpenClientImpl
 * @date 2024/6/18 13:37
 * @description：开放平台客户端处理
 */
@Slf4j
public class OpenClientImpl implements OpenClient {

    /**
     * 缓存客户端
     */
    private final OpenCache openCache;

    /**
     * 开放平台配置
     */
    private final OpenConfig openConfig;

    public OpenClientImpl() {
        this.openCache = OpenCacheRegistry.getCache();
        this.openConfig = OpenConfigProviderRegistry.getProvider().getConfig();
    }

    @Override
    public <input extends BaseRequest, output> OpenResult<output> execute(ClientExecutionParams<input, output> requestParams) {
        input request = requestParams.getRequest();
        HttpResponseHandler<output> responseHandler = requestParams.getResponseHandler();

        // 生成post请求
        String url = openConfig.getUrl() + request.getApiPath();

        HttpRequest post = HttpUtil.createPost(url);
        // 设置连接超时时间（单位：毫秒）
        post.setConnectionTimeout(openConfig.getConnectionTimeoutInMillis());
        // 设置读取超时时间（单位：毫秒）
        post.setReadTimeout(openConfig.getReadTimeoutInMillis());

        // 设置访问token
        // 如果需要认证，则获取token
        if (requestParams.getIsAuth()) {
            OpenResult<AuthResponse> openResult = TokenUtil.getToken();
            if (!openResult.isSuccess()) {
                return OpenResult.fail(openResult);
            }

            post.header(CommonConstants.AUTHORIZATION, CommonConstants.BEARER + openResult.getData().getAccessToken());
        }

        // 设置请求参数
        this.setRequestParams(post, request, requestParams.isBody());
        log.info("发送请求: Url: {}, Request: {}", url, JSONUtil.toJsonStr(request));
        if (openConfig.isConsoleLogEnabled()) {
            System.out.println("发送请求，url = " + url + ", request = " + JSONUtil.toJsonStr(request));
        }


        // 发送请求
        HttpResponse httpResponse = post.execute();

        // 响应解析
        OpenResult<output> result = responseHandler.handle(httpResponse);
        log.info("请求响应: response: {}", JSONUtil.toJsonStr(result));

        if (openConfig.isConsoleLogEnabled()) {
            System.out.println("请求响应，response = " + JSONUtil.toJsonStr(result));
        }

        return result;
    }


    /**
     * 设置请求参数
     *
     * @param <input>
     * @param httpRequest
     * @param request
     * @param isBody
     */
    private <input extends BaseRequest> void setRequestParams(HttpRequest httpRequest, input request, boolean isBody) {
        // 设置body参数
        if (isBody) {
            httpRequest.body(JSONUtil.toJsonStr(request));
            return;
        }

        JSONObject jsonObject = JSONUtil.parseObj(request);

        // 修复新版hutool导致的请求参数有误问题，会把对象参数转成 a=xxx,b=xxx 的转码形式发送请求
        // 原因：新版的JSONObject实现了Iterable接口，导致转换问题
        Map<String, Object> paramMap = new HashMap<>();
        for (String name : jsonObject.keySet()) {
            Object value = jsonObject.get(name);

            // 忽略非法的form表单项内容;
            if (StrUtil.isBlank(name) || ObjectUtil.isNull(value)) {
                continue;
            }

            // 都转成string格式，如果是JSON类型的字段，会变成jsonStr
            paramMap.put(name, StrUtil.toString(value));
        }
        httpRequest.form(paramMap);
    }
}
